(ns clojure-demo.lifes
  :use [clojure.pprint])

(defn neighbours
    [[x y]]
    (for [dx [-1 0 1] dy [-1 0 1] :when (not= 0 dx dy)]
        [(+ dx x) (+ dy y)]))

(defn step
    "Yields the next state of the world"
    [cells]     ;cells，序列，元素为活细胞的坐标。
    (set (for [ [loc n] (frequencies (mapcat neighbours cells))     ;解构，loc是细胞的坐标，n是细胞出现的次数。
                :when (or (= n 3) (and (= n 2) (cells loc)))    ]   ;出现3次说明有3个活邻居，活着或复活。出现2次，且自身活着，保持活着。
            loc)))      ;返回活着的细胞。

;;;;;;;;;;;;
;生成细胞图（二维向量）。
(defn empty-board
    "Creates a rectangular empty board of the specified width and height."
    [w h]
    (vec (repeat w (vec (repeat h nil)))))

;--
;初始化细胞图。
(defn populate
    "Turns :on each of the cells specified as [y, x] coordinates."
    [board living-cells]   ;living-cells，活格子的坐标
    (reduce (fn [board coordinates]
                (assoc-in board coordinates :on))
            board
            living-cells))

(->> (iterate step #{[2 0] [2 1] [2 2] [1 2] [0 1]}) 
     (drop 8)   ;移除前8代细胞图
     first   ;取第9代细胞图
     (populate (empty-board 6 6))
     pprint)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;自定义邻居的计算方式（根据neighbours参数），
;自定义存活方式（根据survive?参数），
;自定义复活方式（根据birth?参数），
;返回配置后的繁衍函数。

;繁衍函数工厂：
(defn stepper
    "Returns a step function for Life-like cell automata.
    neighbours takes a location and return a sequential collection of locations. 
    survive? and birth? are predicates on the number of living neighbours."

    [neighbours birth? survive?]
    (fn [cells]
        (set (for [[loc n] (frequencies (mapcat neighbours cells))
                :when (if (cells loc) (survive? n) (birth? n))]
                loc))))

(defn hex-neighbours
    [[x y]]
    (for [dx [-1 0 1] dy (if (zero? dx) [-2 2] [-1 1])]
         [(+ dx x) (+ dy y)]))

(def hex-step (stepper hex-neighbours #{2} #{3 4}))
;= ; this configuration is an oscillator of period 4

;（oscillator，振荡器。）


(hex-step #{[0 0] [1 1] [1 3] [0 4]})
;= #{[1 -1] [2 2] [1 5]}

(hex-step *1)
;= #{[1 1] [2 4] [1 3] [2 0]}

(hex-step *1)
;= #{[1 -1] [0 2] [1 5]}

(hex-step *1)
;= #{[0 0] [1 1] [1 3] [0 4]}

(stepper #(filter (fn [[i j]] (and (< -1 i w) (< -1 j h)))
                  (neighbours %))
         #{2 3} #{3})

